﻿using System.Reflection.Emit;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Reflection;
namespace ProxyViewModel.Emitors {
	public class ValidatableViewModelEmitor : EmitorBase {
		private static MethodAttributes _attributes = MethodAttributes.Public | MethodAttributes.NewSlot | MethodAttributes.Virtual |
				MethodAttributes.SpecialName | MethodAttributes.HideBySig;

		public ValidatableViewModelEmitor(TypeBuilder typeBuilder, Type operatable, IDictionary<string, object> parameters) :
			base(typeBuilder, operatable, parameters) { }

		protected override void EmitContent() {
			TypeBuilder.AddInterfaceImplementation(typeof(IValidatableViewModel));
			FieldBuilder validationMessagesField = TypeBuilder.DefineField("_validationMessages", typeof(ObservableCollection<ValidationMessage>), FieldAttributes.Private);
			FieldBuilder isValidField = TypeBuilder.DefineField("_isViewModelValid", typeof(bool), FieldAttributes.Private);

			CallMethodOnConstruction(InitializeValidationMessages(validationMessagesField, isValidField));
			DefineValidationMessagesProperty(validationMessagesField);
			DefineIsViewModelValidProperty(isValidField);
		}

		private MethodBuilder InitializeValidationMessages(FieldBuilder validationMessagesField, FieldBuilder isValidField) {
			MethodBuilder builder = TypeBuilder.DefineMethod("InitializeValidationMessagesAndIsValid", MethodAttributes.Private);
			ILGenerator il = builder.GetILGenerator();
			il.Emit(OpCodes.Nop);
			il.Emit(OpCodes.Ldarg_0);
			il.Emit(OpCodes.Newobj, typeof(ObservableCollection<ValidationMessage>).GetConstructor(Type.EmptyTypes));
			il.Emit(OpCodes.Stfld, validationMessagesField);
			il.Emit(OpCodes.Nop);
			il.Emit(OpCodes.Ldarg_0);
			il.Emit(OpCodes.Ldc_I4_1);
			il.Emit(OpCodes.Stfld, isValidField);
			il.Emit(OpCodes.Ret);
			return builder;
		}

		private void DefineIsViewModelValidProperty(FieldBuilder isValidField) {
			PropertyBuilder isValidProperty = TypeBuilder.DefineProperty("IsViewModelValid", PropertyAttributes.HasDefault, typeof(bool), new Type[] { typeof(bool) });
			MethodBuilder getMethod = TypeBuilder.DefineMethod("get_IsViewModelValid", _attributes, typeof(bool), Type.EmptyTypes);
			ILGenerator il = getMethod.GetILGenerator();
			il.DeclareLocal(typeof(bool));
			il.Emit(OpCodes.Nop);
			il.Emit(OpCodes.Ldarg_0);
			il.Emit(OpCodes.Ldfld, isValidField);
			il.Emit(OpCodes.Nop);
			il.Emit(OpCodes.Ret);

			isValidProperty.SetSetMethod(PropertyValidationEmitor.GenerateObservableSetter(isValidProperty, isValidField,
				TypeBuilder, GetParameter<MethodInfo>(Parameters.RaisePropertyChangedMethodKey), _attributes));
			isValidProperty.SetGetMethod(getMethod);
		}

		private void DefineValidationMessagesProperty(FieldBuilder validationMessagesField) {
			PropertyBuilder propertyBuilder = TypeBuilder.DefineProperty("ValidationMessages", PropertyAttributes.HasDefault, typeof(ObservableCollection<ValidationMessage>),
				new Type[] { typeof(ObservableCollection<ValidationMessage>) });

			MethodBuilder methodBuilder = TypeBuilder.DefineMethod("get_ValidationMessages", _attributes, typeof(ObservableCollection<ValidationMessage>), Type.EmptyTypes);
			ILGenerator il = methodBuilder.GetILGenerator();
			il.Emit(OpCodes.Nop);
			il.Emit(OpCodes.Ldarg_0);
			il.Emit(OpCodes.Ldfld, validationMessagesField);
			il.Emit(OpCodes.Nop);
			il.Emit(OpCodes.Ret);
			propertyBuilder.SetGetMethod(methodBuilder);
		}
	}
}
